﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Timers;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.Configuration.Assemblies;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;

namespace PurpleKnife.Library
{
    // Class to test TCP reachability by connection attempt
    // Works asynchronously (non-blocking)
    public class CheckTCP
    {

        //[DllImport("kernel32.dll", SetLastError = true)]
        //[ResourceExposure(ResourceScope.Process)]
        //internal static unsafe extern bool CancelIoEx(SafeFileHandle handle, NativeOverlapped* lpOverlapped);


        #region "Member Variables"
            
            // socket connect timeout value
            private int _timeout = 2000;
        
        #endregion

        #region "Public Interface"

            // check complete event
            public event EventHandler<CheckCompletedEventArgs> CheckCompleted;

            // perform TCP connect check
            public void PerformCheck(EndPoint Socket)
            {
                // create state object for async operation
                StateObject state = new StateObject();
                // store self reference
                state.Me = this;
                // create working socket
                state.WorkSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                // disable SO_LINGER on socket
                //LingerOption lo = new LingerOption(false, 0);
                //state.WorkSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, lo);
                state.WorkSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
                // create timeout timer
                state.TimeoutTimer = new System.Timers.Timer();
                // set timeout interval
                state.TimeoutTimer.Interval = _timeout;
                // create timeout elapsed event
                state.TimeoutTimer.Elapsed += (sender, e) => timeout_elapsed(sender, e, state);
                // perform async connection attempt
                state.WorkSocket.BeginConnect(Socket, new AsyncCallback(ConnectCallback), state);
                // start the timer
                state.TimeoutTimer.Start();
            }

        #endregion

        #region "Callbacks"

            // connection callback
            private static void ConnectCallback(IAsyncResult ar)
            {
                // obtain state object
                StateObject state = (StateObject) ar.AsyncState;
                // stop timer
                state.TimeoutTimer.Stop();
                // dispose timer
                state.TimeoutTimer.Dispose();

                // complete event arguments object
                CheckCompletedEventArgs ea = new CheckCompletedEventArgs();

                if (state.WorkSocket.Connected)
                {
                    // connection successfull; complete async connection attempt
                    state.WorkSocket.EndConnect(ar);
                    // close socket
                    state.WorkSocket.Close();
                    // signal success
                    ea.Success = true;
                }
                else
                {
                    // connection failed; close socket
                    state.WorkSocket.Close();
                    // signal failure
                    ea.Success = false;
                }
                
                // raise completed event
                state.Me.OnCheckCompleted(ea);
                // dispose socket
                state.WorkSocket.Dispose();
                // destroy self reference in state object
                state.Me = null;
            }

            // timeout callback
            private void timeout_elapsed(object sender, EventArgs e, StateObject state)
            {
                // stop the timeout timer
                state.TimeoutTimer.Stop();
                // close socket and cancel all async operations
                state.WorkSocket.Close();
                
            }
        #endregion

        #region "Helper Functions"

            // raiseevent helper
            protected virtual void OnCheckCompleted(CheckCompletedEventArgs e)
            {
                // raise the event
                EventHandler<CheckCompletedEventArgs> handler = CheckCompleted;
                if (handler != null)
                {
                    handler(this, e);
                }
            }

        #endregion

        #region "Event Arg & State Objects"

            // checkcompleted event args class
            public class CheckCompletedEventArgs : EventArgs
            {
                // test success true/false
                public bool Success { set; get; }
            }

            // async state object
            public class StateObject
            {
                // EndPoint
                public EndPoint EndpointSocket { set; get; }
                // working socket object
                public Socket WorkSocket { set; get; }
                // timeout timer object
                public System.Timers.Timer TimeoutTimer { set; get; }
                // self reference
                public CheckTCP Me { set; get; }
            }
        #endregion

    }
}
